#include <user.h>
#include <ts7200.h>
#include <defs.h>
#include <vic.h>
#include <uart.h>
#include <iobuf.h>
#include <isr.h>
#include <terminal.h>
#include <heap.h>
#include <messages.h>

void TerminalRxNotifier() {
  int i, c, ptr;
  int server_tid;
  terminal_rx_req msg;
  msg.type = MSG_RX_DATA;
  uart *const uart2 = (uart*)( UART2_BASE );
  
  Receive(&server_tid, NULL, 0);
  Reply(server_tid, NULL, 0);

  for ( ; ; ) {
    int int_flags = AwaitEvent(EVENT_UART2_RX, NULL, 0);
    ptr = 0;
    if (int_flags & RIS_MASK) {
      // read 8 bytes from buffer
      for (i = 0; i < 8; i++) {
        c = uart2->data;
        if (uart2->rx_sts & (FE_MASK | PE_MASK | BE_MASK | OE_MASK)) {
          bwprintf(COM2, "RX error: %x\r\n", uart2->rx_sts);
          Panic("TerminalRxNotifier", "RX error bits set");
        }
        msg.ch[ptr++] = c;
      }
    } else if (int_flags & RTIS_MASK) {
      // read from FIFO until empty
      while (!(uart2->flag & RXFE_MASK)) {
        c = uart2->data;
        if (uart2->rx_sts & (FE_MASK | PE_MASK | BE_MASK | OE_MASK)) {
          bwprintf(COM2, "RX error: %x\r\n", uart2->rx_sts);
          Panic("TerminalRxNotifier", "RX error bits set");
        }
        msg.ch[ptr++] = c;
      }
    }

    // send data to server
    msg.len = ptr;
    Send( server_tid, (char*)&msg, sizeof(terminal_rx_req), NULL, 0 );
  }
  Panic("TerminalRxNotifier", "Chuck Norris!");
}

void TerminalRxServer() {
  int i, i_min;
  iobuf buf;
  IobufInit(&buf);

  // Getc() queue
  iobuf request_queue;
  IobufInit(&request_queue);

  terminal_rx_req msg;
  rx_reply reply;
  
  int sender_tid;
  uart *const uart2 = (uart*)( UART2_BASE );

  int notifier_tid = Create( 9, &TerminalRxNotifier, "TerminalRxNotifier" );

  // notify parent
  int parent_tid;
  Receive(&parent_tid, NULL, 0);
  Reply(parent_tid, NULL, 0);

  Send(notifier_tid, NULL, 0, NULL, 0);
  
  RegisterAs("terminal-rx-server");
  
  for ( ; ; ) {
    Receive(&sender_tid, (char*)&msg, sizeof(terminal_rx_req));
    switch (msg.type) {
    case MSG_RX_DATA:
      Reply(notifier_tid, NULL, 0);
      
      // notifier has data for me
      if (IobufFull(&buf)) {
        while (!IobufEmpty(&buf)) {
          int a;
          IobufDequeue(&buf, &a);
          bwprintf(COM2, "%c\r\n", a);
        }
        Panic("TerminalRxServer", "IO buffer full");
      }
      
      for (i = 0; i < msg.len; i++) {
        IobufEnqueue( &buf, msg.ch[i] );
      }
      if (!IobufEmpty(&request_queue)) {
        IobufDequeue(&request_queue, &i_min);
        IobufDequeue(&buf, &(reply.ch));
        Reply( i_min, (char*)&reply, sizeof(rx_reply) );
      }
      break;
    case MSG_RX_HASC:
      reply.ch = !IobufEmpty(&buf);
      Reply( sender_tid, (char*)&reply, sizeof(rx_reply) );
      break;
    case MSG_RX_GETC:
      IobufEnqueue(&request_queue, sender_tid);
      if (!IobufEmpty(&buf)) {
        IobufDequeue(&request_queue, &i_min);
        IobufDequeue(&buf, &(reply.ch));
        Reply( i_min, (char*)&reply, sizeof(rx_reply) );
      }
    }
    uart2->ctrl |= (RIEN_MASK | RTIEN_MASK);
  }
  Panic("TerminalRxServer", "Chuck Norris!");
}

// UART2 Transmit tasks
void TerminalTxNotifier() {
  int int_flags;
  
  int comserver_tid;
  Receive(&comserver_tid, NULL, 0);
  Reply(comserver_tid, NULL, 0);
  
  for ( ; ; ) {
    int_flags = AwaitEvent( EVENT_UART2_TX, NULL, 0 );
    if (int_flags & TIS_MASK) {
      Send(comserver_tid, NULL, 0, NULL, 0);
    }
  }
  Exit();
}

void TerminalTransmitter() {
  int i, c;
  iobuf buf;
  IobufInit(&buf);
  char reply[IO_BUF_SIZE];
  int replylen;

  uart *const uart2 = (uart*)( UART2_BASE );

  int notifier_tid = Create(9, &TerminalTxNotifier, "TerminalTxNotifier");

  int server_tid;
  Receive(&server_tid, NULL, 0);
  Reply(server_tid, NULL, 0);
  
  Send(notifier_tid, NULL, 0, NULL, 0);

  for ( ; ; ) {
    if (!IobufEmpty(&buf)) {
      // wait on TX interrupt
      Receive(&notifier_tid, NULL, 0);
      Reply(notifier_tid, NULL, 0);
      
      if (IobufSize(&buf) <= 8) {
        while (!IobufEmpty( &buf )) {
          IobufDequeue( &buf, &c );
          uart2->data = c;
        }
      } else {
        for (i = 0; i < 8; i++) {
          IobufDequeue( &buf, &c );
          uart2->data = c;
        }
        uart2->ctrl |= TIEN_MASK;
      }
    } else {
      // request data from main server
      if (IobufFull(&buf))
        Panic("TerminalRxServer", "IO buffer full");
      replylen = Send(server_tid, NULL, 0, reply, IO_BUF_SIZE);
      for (i = 0; i < replylen; i++) {
        c = reply[i];
        IobufEnqueue(&buf, c);
      }
      
      // bytes arrive to transmit
      if (uart2->flag & TXBUSY_MASK) {
        uart2->ctrl |= TIEN_MASK;
      } else {
        if (IobufSize(&buf) <= 16) {
          while (!IobufEmpty( &buf )) {
            IobufDequeue( &buf, &c );
            uart2->data = c;
          }
        } else {
          for (i = 0; i < 16; i++) {
            IobufDequeue( &buf, &c );
            uart2->data = c;
          }
          uart2->ctrl |= TIEN_MASK;
        }
      }
    }
  }
  Panic("TerminalTransmitter", "Chuck Norris!");
}

void TerminalTxServer() {
  int i, c;
  tx_req msg;
  char reply[IO_BUF_SIZE];
  int replylen;
  int txmitter_ready = FALSE;     // has \0 been sent?
  int txmitter_waiting = FALSE;   // is the transmitter waiting?

  int sender_tid;
  iobuf buf;
  IobufInit(&buf);
  
  int txmitter_tid = Create( 8, &TerminalTransmitter, "TerminalTransmitter" );
  
  // int printer_tid = WhoIs("printer");

  // notify parent
  int parent_tid;
  Receive(&parent_tid, NULL, 0);
  Reply(parent_tid, NULL, 0);
  
  Send(txmitter_tid, NULL, 0, NULL, 0);
  
  RegisterAs("terminal-tx-server");
  
  for ( ; ; ) {
    Receive( &sender_tid, (char*)&msg, sizeof(tx_req));
    if (sender_tid == txmitter_tid) {
      // transmitter data request received!
      if (txmitter_ready && !IobufEmpty(&buf)) {
        replylen = IobufSize( &buf );
        for (i = 0; i < replylen; i++) {
          IobufDequeue( &buf, &c );
          reply[i] = c;
        }
        txmitter_ready = FALSE;
        txmitter_waiting = FALSE;
        Reply( txmitter_tid, reply, replylen );
      } else {
        // no data available, so block it for now
        txmitter_waiting = TRUE;
      }
    } else {// else if (sender_tid == printer_tid) {
      // system call request received!
      if (IobufFull(&buf)) {
        while (!IobufEmpty(&buf)) {
          int a;
          IobufDequeue(&buf, &a);
          bwprintf(COM2, "%c\r\n", a);
        }
        Panic("TerminalTxServer", "IO buffer full");
      }
      Reply(sender_tid, NULL, 0);
      
      char *str = msg.str;
      // bwprintf(COM1, str);
      // bwprintf(COM1, "\r\n");
      for (i = 0; i < msg.len; i++) {
          IobufEnqueue(&buf, *str++);
      }
      txmitter_ready = TRUE;
      
      // if the transmitter is waiting, we respond to it here
      if (txmitter_ready && txmitter_waiting) {
        replylen = IobufSize( &buf );
        for (i = 0; i < replylen; i++) {
          IobufDequeue( &buf, &c );
          reply[i] = c;
        }
        txmitter_ready = FALSE;
        txmitter_waiting = FALSE;
        Reply( txmitter_tid, reply, replylen );
      }
    }
    /*
    else {
      bwprintf(COM2, "Task %d called TerminalTxServer.\r\n", sender_tid);
      Panic("TerminalTxServer", "A call from and invalid task.");
    }
    */
  }
  Panic("TerminalTxServer", "Chuck Norris!");
}
